#include    "scheduler.h"
#include    "../utils/datetime.h"
#include    "../utils/logger.h"

extern "C" {
#include	<luajit.h>
#include	<lualib.h>
#include	<lauxlib.h>
}

Scheduler::Scheduler()
    : _alloc_id(0)
    , _next(-1)
    , _tasks() {}

Scheduler::~Scheduler() {
    for (auto p : _tasks) delete p;
    _tasks.clear();
}

Scheduler & Scheduler::Instance() {
    static Scheduler * g_scheduler = nullptr;
    if (g_scheduler == nullptr) g_scheduler = new Scheduler();
    return *g_scheduler;
}

uint64_t Scheduler::Add(double delay, int proc, bool loop) {
    return __Add(Tick() + delay, loop ? delay : 0, proc);
}

uint64_t Scheduler::Add(int hour, int minute, int second, int proc) {
    DateTime now;
    DateTime today(now.year, now.month, now.day, hour, minute, second);

    double delay = today - now;
    if (delay < 0) delay += 24 * 3600 * 1000;
    return __Add(Tick() + delay, 24 * 3600 * 1000, proc);
}

bool Scheduler::IsValid(uint64_t id) {
    for (auto p : _tasks) {
        if (p->id == id) return !(p->cancelled);
    }
    return false;
}

double Scheduler::GetRemainTime(uint64_t id) {
    for (auto p : _tasks) {
        if (p->id == id) return (p->cancelled ? -1 : p->trigger - Tick());
    }
    return -1;
}

void Scheduler::Cancel(uint64_t id) {
    for (auto p : _tasks) {
        if (p->id == id) {
            p->cancelled = true;
            break;
        }
    }
}

void Scheduler::Breath(struct lua_State * lua) {
    double tick = Tick();
    if (_next < 0 || _next > tick) return;

    for (auto it = _tasks.begin(); it != _tasks.end();) {
        Task * task = *it;
        if (task->trigger > tick) break;

        it = _tasks.erase(it);
        if (task->cancelled) {
            delete task;
            continue;
        }

        int top = lua_gettop(lua);
        lua_rawgeti(lua, LUA_REGISTRYINDEX, task->proc);
        if (lua_pcall(lua, 0, 0, top) != 0) {
            Logger::Instance().Error("Scheduler invoke error : %s\n", lua_tostring(lua, -1));
        }
        lua_settop(lua, top);
        
        if (task->loop_delay > 0) {
            task->trigger += task->loop_delay;
            _tasks.push_back(task);
        } else {
            delete task;
        }
    }

    _tasks.sort([](Task * l, Task * r) { return l->trigger < r->trigger; });
    _next = _tasks.size() > 0 ? _tasks.front()->trigger : -1;
}

uint64_t Scheduler::__Add(double trigger, double loop, int proc) {
    Task * task = new Task();
    task->id = ++_alloc_id;
    task->cancelled = false;
    task->trigger = trigger;
    task->loop_delay = loop;
    task->proc = proc;

    _tasks.push_back(task);
    _tasks.sort([](Task * l, Task * r) { return l->trigger < r->trigger; });
    _next = _tasks.front()->trigger;

    return task->id;
}

